﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using NLog;

namespace TfsGiggle.Core
{
    /// <summary>
    /// Tfs source control helper class
    /// </summary>
    public sealed class TfsSourceControl : IDisposable
    {
        static LoggerWithEventId _logger = (LoggerWithEventId)LogManager.GetCurrentClassLogger(typeof(LoggerWithEventId));
        static readonly Lazy<TfsSourceControl> _instance = new Lazy<TfsSourceControl>(() => new TfsSourceControl());
        ConcurrentDictionary<string, Tuple<ITfsTeamProjectCollection, IVersionControlServer>> _tfsTeamProjects = new ConcurrentDictionary<string, Tuple<ITfsTeamProjectCollection, IVersionControlServer>>();
        ITfsConfigurationServer _tfsConfigurationServer;

        TfsSourceControl() { }

        /// <summary>
        /// Tfs source control instance
        /// </summary>
        public static TfsSourceControl Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        /// <summary>
        /// Opens a connection to a TfsConfigurationServer
        /// </summary>
        /// <param name="tfsConfigurationServer"></param>
        /// <returns></returns>
        internal bool Open(ITfsConfigurationServer tfsConfigurationServer)
        {
            if (tfsConfigurationServer == null) throw new ArgumentNullException("tfsConfigurationServer");
            _logger.Trace("Name:{0}", tfsConfigurationServer.Name);

            _tfsConfigurationServer = tfsConfigurationServer;
            _tfsConfigurationServer.EnsureAuthenticated();
            _logger.Trace("exit");
            return true;
        }

        /// <summary>
        /// Gets a version control server
        /// </summary>
        /// <param name="teamProjectCollectionId"></param>
        /// <returns></returns>
        internal IVersionControlServer GetVersionControlServer(string teamProjectCollectionId)
        {
            Tuple<ITfsTeamProjectCollection, IVersionControlServer> tfsAndVcs;
            _logger.Trace("teamProjectCollectionId:{0}", teamProjectCollectionId);

            if (!_tfsTeamProjects.TryGetValue(teamProjectCollectionId, out tfsAndVcs))
            {
                var tfs = _tfsConfigurationServer.GetTeamProjectCollection(new Guid(teamProjectCollectionId));
                if (tfs == null)
                {
                    _logger.Error("Could not get teamProjectCollection:{0}", teamProjectCollectionId);
                    return null;
                }
                tfs.EnsureAuthenticated();
                var vcs = ((IServiceProvider)tfs).GetService<IVersionControlServer>();
                if (vcs == null)
                {
                    _logger.Error("Could not get a VersionControlServer from teamProjectCollection:{0}", teamProjectCollectionId);
                    return null;
                }
                tfsAndVcs = new Tuple<ITfsTeamProjectCollection, IVersionControlServer>(tfs, vcs);
                _tfsTeamProjects.AddOrUpdate(teamProjectCollectionId, tfsAndVcs, (key, value) => tfsAndVcs);
            }
            _logger.Trace("exit");
            return tfsAndVcs.Item2;
        }

        private void Dispose(bool disposing)
        {
            _logger.Trace("enter");
            if (disposing && _tfsConfigurationServer != null)
            {
                ((IDisposable)_tfsConfigurationServer).Dispose();
                _tfsConfigurationServer = null;
            }
            if (disposing && _tfsTeamProjects != null && _tfsTeamProjects.Count > 0)
            {
                foreach (var tfs in _tfsTeamProjects.Values)
                {
                    ((IDisposable)tfs.Item1).Dispose();
                }
                _tfsTeamProjects = null;
            }
            _logger.Trace("exit");
        }

        /// <summary>
        /// Closes the Tfs connection
        /// </summary>
        public void Dispose()
        {
            _logger.Trace("enter");
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}